package com.iteaj.iot;

import com.iteaj.iot.client.ClientComponent;
import com.iteaj.iot.config.ConnectProperties;
import com.iteaj.iot.server.IotSocketServer;
import com.iteaj.iot.server.ServerComponent;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class DefaultComponentFactory implements ComponentFactory {

    private Map<String, FrameworkComponent> portMaps = new HashMap<>();
    private static ComponentFactory componentFactory = new DefaultComponentFactory();
    private Map<Class<? extends Message>, FrameworkComponent> messageComponentMap = new HashMap(8);

    protected DefaultComponentFactory() { }

    public static ComponentFactory getInstance() {
        return componentFactory;
    }

    @Override
    public int size() {
        return messageComponentMap.size();
    }

    @Override
    public FrameworkComponent get(Class<? extends Message> messageClazz) {
        return messageComponentMap.get(messageClazz);
    }

    @Override
    public List<ClientComponent> clients() {
        return Collections.unmodifiableList(messageComponentMap.values().stream()
                .filter(item -> item instanceof ClientComponent)
                .map(item -> (ClientComponent)item)
                .collect(Collectors.toList()));
    }

    @Override
    public List<ServerComponent> servers() {
        return Collections.unmodifiableList(messageComponentMap.values().stream()
                .filter(item -> item instanceof ServerComponent)
                .map(item -> (ServerComponent)item)
                .collect(Collectors.toList()));
    }

    @Override
    public FrameworkComponent get(Integer port) {
        return portMaps.get(port.toString());
    }

    @Override
    public boolean close(Class<?> messageClazz) {
        FrameworkComponent component = messageComponentMap.remove(messageClazz);
        if(component != null) {
            if(component instanceof IotSocketServer) {
                ConnectProperties config = ((IotSocketServer) component).config();
                portMaps.remove(config.getPort().toString());
            }

            component.close(); return true;
        }

        return false;
    }

    @Override
    public void register(FrameworkComponent component) {
        if(component instanceof IotSocketServer) {
            ConnectProperties config = ((IotSocketServer) component).config();
            FrameworkComponent frameworkComponent = portMaps.get(config.getPort().toString());
            // 已经有相同的端口
            if(frameworkComponent != null) {
                throw new FrameworkException(frameworkComponent.getName()
                        + "和" + component.getName() + "使用同一个端口: " + config.getPort());
            }

            if(config.getPort() <= 0 || config.getPort() > 65535)
                throw new IllegalArgumentException("服务端组件(" + component.getName() + ")使用错误的端口: " + config.getPort());

            portMaps.put(config.getPort().toString(), component);
        }

        Class<? extends Message> messageClass = component.getMessageClass();
        if(messageClass == null) {
            throw new FrameworkException("请指定组件的报文类型："
                    + component.getClass().getSimpleName()+"<M extend ServerMessage>");
        }

        if(get(messageClass) == null) {
            messageComponentMap.put(messageClass, component);
        }
    }

    @Override
    public void start(Object config) {
        messageComponentMap.values().forEach(component -> {
            component.start(config);
        });
    }

    @Override
    public void close() {
        // 停止所有组件
        messageComponentMap.values()
                .forEach(clientComponent -> clientComponent.close());

        // 移除所有组件
        portMaps.clear();
        messageComponentMap.clear();
    }
}
